package dictionary;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class RadiusDict {

	private Map<Integer, String> radiusCommandCodes;

	private Map<String, Integer> radiusCommandNames;

	private Map<Integer, Avp> avpByType;

	private Map<String, Avp> avpByName;
	
	//private String fileName;
	
	private static RadiusDict singleRadDict;
	
	private RadiusDict(String fileName) throws IOException {
		radiusCommandCodes = new HashMap<>();
		radiusCommandNames = new HashMap<>();
		avpByName = new HashMap<>();
		avpByType = new HashMap<>();
		loadFromFile(fileName);
	}
	
	public static synchronized RadiusDict getInstance(String fileName) throws IOException {
		if (singleRadDict == null) {
			singleRadDict = new RadiusDict(fileName);
			return singleRadDict;
		}
		else {
			return singleRadDict;
		}
	}

//	public RadiusDict() {
//		radiusCommandCodes = new HashMap<>();
//		radiusCommandNames = new HashMap<>();
//		avpByName = new HashMap<>();
//		avpByType = new HashMap<>();
//	}

	public int getCommandCode(String commandName) {
		return radiusCommandNames.get(commandName);
	}

	public String getCommandName(int code) {
		return radiusCommandCodes.get(code);
	}

	public int getAvpCode(String name) {
        Avp avp = avpByName.get(name);
        return avp.getAvpCode();
	}
	
	public String getAvpName(int type) {
		Avp avp = avpByType.get(type);
		return avp.getAvpName();
	}
	
	public Avp getAvpByName(String name) {
		return avpByName.get(name);
	}

	public Avp getAvpByType(int type) {
		return avpByType.get(type);
	}

	private void loadFromFile(String fileName) throws IOException {
		
		Path file = Paths.get(fileName);
		
		BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8);

		String line;

		while ((line = reader.readLine()) != null) {
			if (line.startsWith("#")) {
				continue;
			}
			if (line.startsWith("COMMAND")) {
				createRadiusCodes(line);
			}
			if (line.startsWith("ATTRIBUTE")) {
				loadAVPs(line);
			}
		}
		//return this;
	}

	private void loadAVPs(String line) {
		
		Avp avpObj = null;
		
		String[] avp = line.split("\\s{1,}");
		
		if (avp.length == 5) {
			avpObj = new Avp(Integer.parseInt(avp[2]), avp[1], avp[3], true);
			avpObj.setValues(parseAvpValues(avp[4]));
			// avpObj.setValuesConstrained(true);
			avpByName.put(avp[1], avpObj);
			avpByType.put(Integer.parseInt(avp[2]), avpObj);
		}
		
		if (avp.length < 5) {
			avpObj = new Avp(Integer.parseInt(avp[2]), avp[1], avp[3], false);
			avpByName.put(avp[1], avpObj);
			avpByType.put(Integer.parseInt(avp[2]), avpObj);
		}
		// System.out.println(avpObj);
	}

	private Map<String, Integer> parseAvpValues(String string) {
		Map<String, Integer> values = new HashMap<>();
		String tmp = string.substring(1, string.length() - 1);
		String[] tokens = tmp.split(";");
		for (String string2 : tokens) {
			String[] avpValuesTokens = string2.split("=");
			values.put(avpValuesTokens[0], Integer.parseInt(avpValuesTokens[1]));
		}
		return values;
	}

	public void printAvpByName() {
		Set<String> keys = avpByName.keySet();
		Iterator<String> iter = keys.iterator();
		while (iter.hasNext()) {
			String string = (String) iter.next();
			System.out.println(string);
		}
	}

	private void createRadiusCodes(String line) {
		String[] command = line.split("\\s{1,}");
		radiusCommandCodes.put(Integer.parseInt(command[2]), command[1]);
		radiusCommandNames.put(command[1], Integer.parseInt(command[2]));
	}

	public void testCodes() {
		Iterator<Integer> iter = radiusCommandCodes.keySet().iterator();
		while (iter.hasNext()) {
			int i = iter.next();
			System.out.println("Command code = " + i + " : Command string = "
					+ radiusCommandCodes.get(i));
		}
	}

	public void testNames() {
		Iterator<String> iter = radiusCommandNames.keySet().iterator();
		while (iter.hasNext()) {
			String i = iter.next();
			System.out.println("Command name = " + i + " : Command code = "
					+ radiusCommandNames.get(i));
		}
	}

	
	/**
	 * @param args
	 * @throws IOException
	 * @throws InterruptedException
	 */
	/*
	public static void main(String[] args) throws IOException,
			InterruptedException {

		// RadiusDict dict = new
		// RadiusDict().loadFromFile("C:/Users/Dario/aaa-meter-workspace/radius-trening/src/dictionary/RADIUS.dict");
		RadiusDict dict = new RadiusDict().loadFromFile("D:\\workspace\\radius-trening\\src\\dictionary\\RADIUS.dict");
		// System.out.println("########################");
		// dict.testCodes();
		// System.out.println("########################");
		// dict.testNames();
		// System.out.println("########################");
		// System.out.println("########################");
		// System.out.println("Access-Request command code = " +
		// dict.getCommandCode("Access-Request"));
		// System.out.println("Command name for code 1= " +
		// dict.getCommandName(1));
		// System.out.println("#####################################");
		// dict.printAvpByName();
		Avp avp = dict.getAvpByName("User-Name");
		System.out.println(avp);
		avp = dict.getAvpByType(6);
		if (avp.hasPredefinedValues()) {

			Map<String, Integer> map = avp.getValues();
			Set<String> keys = map.keySet();
			Iterator<String> iter = keys.iterator();

			while (iter.hasNext()) {
				String tmp = iter.next();
				System.out.println("Value name = " + tmp + " value code = "
						+ map.get(tmp));
			}
		} else {
			System.out.println("values not contstrained");
		}
		System.out.println(avp);
		// Avp avp = dict.getAvpByType(1);
		// avp.toString();
	}
*/
}
